package com.wei.czz.framework.common.manager;

import com.wei.czz.common.constant.Constant;
import com.wei.czz.common.constant.EntityConstant;
import com.wei.czz.common.constant.RedisConstant;
import com.wei.czz.common.dto.common.limitConfig.LimitConfigDto;
import com.wei.czz.common.dto.common.limitConfig.LimitConfigEnumsDto;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.enums.common.limitConfig.LimitConfigTypeEnum;
import com.wei.czz.common.enums.common.limitConfig.LimitConfigUnitEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.po.UserPo;
import com.wei.czz.common.tool.PageDto;
import com.wei.czz.common.utils.CacheKeyUtils;
import com.wei.czz.common.utils.CopyUtils;
import com.wei.czz.common.utils.SecurityUtils;
import com.wei.czz.common.vo.common.BaseVo;
import com.wei.czz.common.vo.common.limitConfig.LimitConfigVo;
import com.wei.czz.common.vo.common.UpdateStatusVo;
import com.wei.czz.common.vo.common.limitConfig.LimitConfigFormVo;
import com.wei.czz.framework.common.entity.LimitConfigEntity;
import com.wei.czz.framework.common.entity.LimitConfigFlowEntity;
import com.wei.czz.framework.common.helper.CacheHelper;
import com.wei.czz.framework.common.service.AsyncService;
import com.wei.czz.framework.common.service.LimitConfigFlowService;
import com.wei.czz.framework.common.service.LimitConfigService;
import com.wei.czz.framework.common.helper.LimitHelper;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2024-06-07 20:54:22
 * className: LimitConfigManager
 * version: 1.0
 * description:
 */
@Component
@AllArgsConstructor
public class LimitConfigManager {

    private static final Logger log = LoggerFactory.getLogger(LimitConfigManager.class);

    private final LimitHelper limitHelper;

    private final LimitConfigService limitConfigService;

    private final LimitConfigFlowService limitConfigFlowService;

    private final AsyncService asyncService;

    private final RedissonClient redissonClient;

    private final CacheHelper cacheHelper;

    public void saveLimit(Long id, LimitConfigVo limitConfigVo) {
        // 获取正在保存限流配置的用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        String lockKey = RedisConstant.LOCK + EntityConstant.LIMIT_CONFIG + limitConfigVo.getRequestKey() +
                Constant.SPLIT + limitConfigVo.getType();
        RLock lock = redissonClient.getLock(lockKey);
        boolean bool = lock.tryLock();
        log.info("保存限流配置，尝试加锁完成。lockKey={} bool={}", lockKey, bool);
        if (!bool) {
            throw new CzzException(ResultEnum.TRY_LOCK_FAIL.getCode(), "其他用户正在操作中，请稍后重试");
        }

        LimitConfigEntity newLimitConfig;
        try {

            LimitConfigEntity limitConfig = null;
            if (Objects.nonNull(id)) {
                /*
                    获取限流配置
                 */
                limitConfig = limitConfigService.get(id);

                if (!limitConfig.getRequestKey().equals(limitConfigVo.getRequestKey())) {
                    log.info("添加限流配置，限流配置对象的接口路径与参数传递的接口路径不一致");
                    throw new CzzException(ResultEnum.ILLEGAL_FAIL);
                }
                if (!limitConfig.getType().equals(limitConfigVo.getType())) {
                    log.info("添加限流配置，限流配置对象的限流类型与参数传递的限流类型不一致");
                    throw new CzzException(ResultEnum.ILLEGAL_FAIL);
                }
                // 属性值覆盖
                limitConfig.setTimeNum(limitConfigVo.getTimeNum())
                        .setTimeType(limitConfigVo.getTimeType())
                        .setWindowNum(limitConfigVo.getWindowNum())
                        .setMaxNum(limitConfigVo.getMaxNum())
                        .setLimitTime(limitConfigVo.getLimitTime())
                        .setMessage(limitConfigVo.getMessage())
                        .setVersion(limitConfig.getVersion() + 1)
                        .setStatus(CommonEnum.ZERO.getValue())
                        .setUpdateTime(date)
                        .setUpdateUser(userPo.getUsername())
                        .setUpdateUserId(userPo.getUserId());
            }
            if (Objects.isNull(limitConfig)) {
                // 映射
                limitConfig = CopyUtils.map(limitConfigVo, LimitConfigEntity.class);
                limitConfig.setId(null)
                        .setVersion(CommonEnum.ONE.getValue())
                        .setDeleteStatus(CommonEnum.ZERO.getValue())
                        .setUpdateTime(date)
                        .setUpdateUser(StringUtils.EMPTY)
                        .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                        .setCreateTime(date)
                        .setCreateUser(userPo.getUsername())
                        .setCreateUserId(userPo.getUserId());
            }

            /*
                保存限流配置
             */
            limitConfigService.saveOrEdit(limitConfig);

            newLimitConfig = limitConfig;

        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

        // 异步操作
        asyncService.execute(() -> {
            /*
                重置限流配置流水状态
             */
            if (Objects.nonNull(id)) {
                limitConfigFlowService.resetUse(id);
            }
            /*
                保存限流配置流水
             */
            // 映射
            LimitConfigFlowEntity limitConfigFlow = CopyUtils.map(newLimitConfig, LimitConfigFlowEntity.class);
            limitConfigFlow.setId(null)
                    .setMasterId(newLimitConfig.getId())
                    .setStatus(CommonEnum.ZERO.getValue())
                    .setUpdateUser(StringUtils.EMPTY)
                    .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                    .setCreateUser(userPo.getUsername())
                    .setCreateUserId(userPo.getUserId());
            limitConfigFlowService.saveOrEdit(limitConfigFlow);

            /*
                增加系统限流规则
             */
            limitHelper.addLimitRule(newLimitConfig);
        });
    }

    public PageDto<LimitConfigDto> getLimitConfigPageList(LimitConfigFormVo limitConfigFormVo) {
        // 获取请求用户主键
        Long optUserId = SecurityUtils.getUserId();

        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.LIMIT_CONFIG);
        cacheHelper.cache(cacheKey, limitConfigFormVo);

        /*
            分页获取限流配置
         */
        PageDto<LimitConfigEntity> pageDto = limitConfigService.getPageList(limitConfigFormVo);
        if (pageDto.isEmpty()) {
            return pageDto.rebuild();
        }

        List<LimitConfigEntity> limitConfigList = pageDto.getList();

        // 列表映射
        List<LimitConfigDto> limitConfigDtoList = CopyUtils.mapList(limitConfigList, LimitConfigDto.class);
        // 封装返回
        return pageDto.rebuild(limitConfigDtoList);
    }

    public PageDto<LimitConfigDto> getLimitConfigHistoryPageList(Long masterId, BaseVo baseVo) {

        /*
            分页获取限流配置历史记录
         */
        PageDto<LimitConfigFlowEntity> pageDto = limitConfigFlowService.getPageList(masterId, baseVo);
        if (pageDto.isEmpty()) {
            return pageDto.rebuild();
        }

        List<LimitConfigFlowEntity> limitConfigFlowList = pageDto.getList();

        // 列表映射
        List<LimitConfigDto> limitConfigDtoList = CopyUtils.mapList(limitConfigFlowList, LimitConfigDto.class);
        // 封装返回
        return pageDto.rebuild(limitConfigDtoList);
    }

    public LimitConfigEnumsDto getEnums() {
        // 获取请求用户主键
        Long optUserId = SecurityUtils.getUserId();

        /*
            获取缓存
         */
        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.LIMIT_CONFIG);
        LimitConfigFormVo limitConfigFormVo = cacheHelper.get(cacheKey, LimitConfigFormVo.class);

        LimitConfigEnumsDto limitConfigEnumsDto = new LimitConfigEnumsDto();
        limitConfigEnumsDto.setTimeTypeEnumList(LimitConfigUnitEnum.getList())
                .setTypeEnumList(LimitConfigTypeEnum.getList())
                .setStatusEnumList(CommonEnum.getStatusList())
                .setLimitConfigFormVo(limitConfigFormVo);

        return limitConfigEnumsDto;
    }

    public Long checkLimitConfig(String requestKey, Integer type) {

        /*
            获取限流配置
         */
        List<LimitConfigEntity> limitConfigList = limitConfigService.findList(
                Collections.singletonList(requestKey),
                type
        );

        if (limitConfigList.isEmpty()) {
            log.info("没有匹配到限流配置");
            return null;
        }
        // 返回已经存在的限流配置主键
        Long id = limitConfigList.get(0).getId();
        log.info("限流配置已存在。id={}", id);
        return id;
    }


    public void updateLimitConfig(LimitConfigVo limitConfigVo) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        /*
            获取限流配置
         */
        LimitConfigEntity limitConfig = limitConfigService.get(limitConfigVo.getId());
        if (!limitConfig.getRequestKey().equals(limitConfigVo.getRequestKey())) {
            log.info("修改限流配置，限流配置对象的接口路径与参数传递的接口路径不一致");
            throw new CzzException(ResultEnum.ILLEGAL_FAIL);
        }
        if (!limitConfig.getType().equals(limitConfigVo.getType())) {
            log.info("修改限流配置，限流配置对象的限流类型与参数传递的限流类型不一致");
            throw new CzzException(ResultEnum.ILLEGAL_FAIL);
        }
        if (!limitConfig.getVersion().equals(limitConfigVo.getVersion())) {
            log.info("限流配置版本号不相同。db.version={} param.version={}", limitConfig.getVersion(),
                    limitConfigVo.getVersion());
            throw new CzzException(ResultEnum.REFRESH);
        }
        // 属性值覆盖
        limitConfig.setTimeNum(limitConfigVo.getTimeNum())
                .setTimeType(limitConfigVo.getTimeType())
                .setWindowNum(limitConfigVo.getWindowNum())
                .setMaxNum(limitConfigVo.getMaxNum())
                .setLimitTime(limitConfigVo.getLimitTime())
                .setMessage(limitConfigVo.getMessage())
                .setVersion(limitConfig.getVersion() + 1)
                .setStatus(limitConfigVo.getStatus())
                .setUpdateTime(date)
                .setUpdateUser(userPo.getUsername())
                .setUpdateUserId(userPo.getUserId());

        /*
            修改限流配置
         */
        int count = limitConfigService.optimisticUpdate(limitConfig);
        if (count == 0) {
            throw new CzzException(ResultEnum.REFRESH);
        }

        // 异步操作
        asyncService.execute(() -> {
            /*
                重置限流配置流水状态
             */
            limitConfigFlowService.resetUse(limitConfig.getId());

            /*
                保存限流配置流水
             */
            // 映射
            LimitConfigFlowEntity limitConfigFlow = CopyUtils.map(limitConfig, LimitConfigFlowEntity.class);
            limitConfigFlow.setId(null)
                    .setMasterId(limitConfig.getId())
                    .setStatus(CommonEnum.ZERO.getValue())
                    .setUpdateTime(date)
                    .setUpdateUser(StringUtils.EMPTY)
                    .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                    .setCreateTime(date)
                    .setCreateUser(userPo.getUsername())
                    .setCreateUserId(userPo.getUserId());
            limitConfigFlowService.saveOrEdit(limitConfigFlow);

            if (CommonEnum.ZERO.getValue().equals(limitConfig.getStatus())) {
                // 增加系统限流规则
                limitHelper.addLimitRule(limitConfig);
            } else {
                // 去除系统限流规则
                limitHelper.deleteLimitRule(limitConfig.getRequestKey(), limitConfig.getType());
            }
        });
    }

    public List<String> updateLimitConfigStatus(UpdateStatusVo updateStatusVo) {
        // 获取操纵用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        List<Long> idList = updateStatusVo.getIdList();
        RLock lock = null;
        if (idList.size() > 1) {
            String lockKey = RedisConstant.LOCK + EntityConstant.LIMIT_CONFIG + Constant.SPLIT + "update-status";
            lock = redissonClient.getLock(lockKey);
            boolean bool = lock.tryLock();
            log.info("修改限流配置状态，尝试加锁完成。lockKey={} bool={}", lockKey, bool);
            if (!bool) {
                throw new CzzException(ResultEnum.TRY_LOCK_FAIL.getCode(), "其他用户正在编辑中，请稍后重试");
            }
        }

        List<LimitConfigEntity> updateLimitConfigList = new ArrayList<>();

        try {
            /*
                获取限流配置
             */
            List<LimitConfigEntity> limitConfigList = limitConfigService.getList(idList);
            if (limitConfigList.isEmpty()) {
                throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "您正在编辑的限流配置不存在，请确认");
            }

            for (LimitConfigEntity limitConfig : limitConfigList) {
                if (limitConfig.getStatus().equals(updateStatusVo.getStatus())) {
                    log.info("限流配置对象状态与参数状态相同。id={} status={}", limitConfig.getId(),
                            updateStatusVo.getStatus());
                    continue;
                }

                limitConfig.setVersion(limitConfig.getVersion() + 1)
                        .setStatus(updateStatusVo.getStatus())
                        .setUpdateTime(date)
                        .setUpdateUser(userPo.getUsername())
                        .setUpdateUserId(userPo.getUserId());
                updateLimitConfigList.add(limitConfig);
            }

            if (updateLimitConfigList.isEmpty()) {
                log.info("限流配置数据已变更，无需修改");
                throw new CzzException(ResultEnum.REFRESH.getCode(), "限流配置已变更，请确认后重试");
            }

            /*
                使用乐观锁批量修改限流配置
             */
            limitConfigList = limitConfigService.optimisticBatchUpdate(updateLimitConfigList);
            if (limitConfigList.isEmpty()) {
                log.info("限流配置数据已变更，修改失败");
                throw new CzzException(ResultEnum.REFRESH.getCode(), "限流配置已变更，请确认后重试");
            }
            updateLimitConfigList.clear();
            updateLimitConfigList.addAll(limitConfigList);

        } finally {
            if (Objects.nonNull(lock) && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

        // 异步批量更新限流规则
        asyncService.execute(() -> {
            if (CommonEnum.ZERO.getValue().equals(updateStatusVo.getStatus())) {
                for (LimitConfigEntity limitConfig : updateLimitConfigList) {
                    // 更新限流规则
                    asyncService.execute(() -> {
                        limitHelper.addLimitRule(limitConfig);
                    });
                }
            } else {
                for (LimitConfigEntity limitConfig : updateLimitConfigList) {
                    // 删除限流规则
                    asyncService.execute(() -> {
                        limitHelper.deleteLimitRule(limitConfig.getRequestKey(), limitConfig.getType());
                    });

                }
            }
        });
        // 映射列表并返回
        return CopyUtils.mapList(updateLimitConfigList, limitConfig -> limitConfig.getId().toString());
    }

    public void useLimitConfigFlow(Long id) {
        // 获取操作用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        /*
            获取限流配置流水
         */
        LimitConfigFlowEntity limitConfigFlow = limitConfigFlowService.get(id);

        /*
            获取限流配置
         */
        LimitConfigEntity limitConfig = limitConfigService.get(limitConfigFlow.getMasterId());

        // 属性值覆盖
        limitConfig.setTimeNum(limitConfigFlow.getTimeNum())
                .setTimeType(limitConfigFlow.getTimeType())
                .setWindowNum(limitConfigFlow.getWindowNum())
                .setMaxNum(limitConfigFlow.getMaxNum())
                .setLimitTime(limitConfigFlow.getLimitTime())
                .setMessage(limitConfigFlow.getMessage())
                .setVersion(limitConfig.getVersion() + 1)
                .setStatus(CommonEnum.ZERO.getValue())
                .setUpdateTime(date)
                .setUpdateUser(userPo.getUsername())
                .setUpdateUserId(userPo.getUserId());
        /*
            修改限流配置
         */
        int count = limitConfigService.optimisticUpdate(limitConfig);
        if (count == 0) {
            throw new CzzException(ResultEnum.REFRESH);
        }

        // 异步操作
        asyncService.execute(() -> {
            /*
                重置限流配置流水状态
             */
            limitConfigFlowService.resetUse(limitConfig.getId());

            /*
                更新限流配置流水
             */
            // 映射
            LimitConfigFlowEntity updateLimitConfigFlow = new LimitConfigFlowEntity();
            updateLimitConfigFlow.setId(limitConfigFlow.getId())
                    .setStatus(CommonEnum.ZERO.getValue())
                    .setUpdateTime(date)
                    .setUpdateUser(userPo.getUsername())
                    .setUpdateUserId(userPo.getUserId());
            limitConfigFlowService.saveOrEdit(updateLimitConfigFlow);

            /*
                增加系统限流规则
             */
            limitHelper.addLimitRule(limitConfig);
        });
    }

    public void deleteLimitConfig(List<Long> idList) {
        /*
            获取限流配置
         */
        List<LimitConfigEntity> limitConfigList = limitConfigService.getList(idList);
        if (limitConfigList.isEmpty()) {
            throw new CzzException(ResultEnum.REFRESH);
        }

        List<Long> updateIdList = new ArrayList<>();
        List<LimitConfigEntity> deleteLimitConfigList = new ArrayList<>();

        for (LimitConfigEntity limitConfig : limitConfigList) {
            // 收集主键
            updateIdList.add(limitConfig.getId());

            if (limitConfig.getStatus().equals(CommonEnum.ZERO.getValue())) {
                // 删除配置
                deleteLimitConfigList.add(limitConfig);
            }
        }

        /*
            删除限流配置
         */
        limitConfigService.delete(updateIdList);

        if (!deleteLimitConfigList.isEmpty()) {
            asyncService.execute(() -> {
                for (LimitConfigEntity limitConfig : deleteLimitConfigList) {
                    asyncService.execute(() -> {
                        // 异步删除限流规则
                        limitHelper.deleteLimitRule(limitConfig.getRequestKey(), limitConfig.getType());
                    });
                }
            });
        }
    }

}
